import os
import datetime

from django.shortcuts import render_to_response, get_object_or_404
from django import forms
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.forms import ValidationError
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import logout
from django.utils import simplejson

import fca
from fca.algorithms.exploration.exploration import (NotCounterexample,
                                                    IllegalContextModification,
                                                    NotUniqueAttributeName,
                                                    NotUniqueObjectName)

from models import (Object, Attribute, import_context, AttributeImplication,
                   ObjectImplication, Event, get_csv)
from forms import (ObjectForm, AttributeForm, AttributeImplicationForm,
                  ObjectImplicationForm)
import exploration as e_

APP_NAME = 'exploration'
error_message = None
    
def register_event(user, type, annotation):
    event = Event(type=type, annotation=annotation, author=user)
    event.save()


def main(request):
    """Handles main page of our site"""
    global error_message
    if error_message:
        _msg = error_message[:]
        error_message = None
    else:
        _msg = None
    all_attributeimplications = AttributeImplication.objects.all()
    open_implications = all_attributeimplications.filter(is_confirmed=False)
    confirmed_implications = all_attributeimplications.filter(is_confirmed=True)
    data_dictionary = {
        "objects" : Object.objects.all(),
        "attributes" : Attribute.objects.all(),
        "open_implications" : open_implications,
        "confirmed_implications" : confirmed_implications,
        "message" : _msg,
    }
    return render_to_response(os.path.join(APP_NAME, 'main.html'),
                              data_dictionary,
                              context_instance=RequestContext(request))

def log(request):
    """Handles history page"""
    all_events = Event.objects.all()
    data_dictionary = {
        "events" : all_events,
    }
    return render_to_response(os.path.join(APP_NAME, 'log.html'),
                              data_dictionary)                        

def logout_view(request):
    logout(request)
    return HttpResponseRedirect(reverse('exploration.views.main'))

@login_required
def new_object(request):
    """Handles page where we create new object"""
    if request.method == 'POST':
        form = ObjectForm(request.POST)
        if form.is_valid():
            intent = set()
            for attribute in form.cleaned_data['attributes']:
                intent.add(attribute.name)
            object_name = form.cleaned_data['name']
            try:
                e_.db.check_form_and_add_example(form, object_name, intent)
                annotation = u'New Object: {0}'.format(object_name)
                register_event(request.user, 'obj_add', annotation)
            except IllegalContextModification:
                display_error("""Provided example violates background
                                 knowledge and can not be saved.""")
            except NotUniqueObjectName:
                display_error("""Example name should be unique.""")
            return HttpResponseRedirect(reverse('exploration.views.main'))
    else:
        form = ObjectForm()

    return render_to_response(os.path.join(APP_NAME, 'objects/new.html'),
                              {'form': form,},
                              context_instance=RequestContext(request))

@login_required                              
def edit_object(request, object_id):
    """Handles page where we edit object"""
    object_ = get_object_or_404(Object, pk=object_id)
    old_name = object_.name
    if request.method == 'POST':
        if 'delete' in request.POST:
            object_.delete()
            e_.db.sync_implications()
            register_event(request.user, 'obj_rem', object_.name)
        else:
            form = ObjectForm(request.POST, instance=object_)
            if form.is_valid():
                intent = set()
                for attribute in form.cleaned_data['attributes']:
                    intent.add(attribute.name)
                object_name = form.cleaned_data['name']
                try:
                    e_.db.check_form_and_edit_example(form, 
                                                      object_name, 
                                                      old_name,
                                                      intent)
                    annotation = u'Edited Object: {0}\n'.format(object_name)
                    register_event(request.user, 'obj_edit', annotation)
                except IllegalContextModification:
                    display_error("""Proposed example modification violates 
                                     background knowledge and can not be 
                                     saved.""")
                except NotUniqueObjectName:
                    display_error("""Example name should be unique.""")
        return HttpResponseRedirect('/')
    else:
        form = ObjectForm(instance=object_)

    return render_to_response(os.path.join(APP_NAME, 'objects/edit.html'),
                            {'form': form,
                             'object_id': object_id},
                            context_instance=RequestContext(request))

@login_required    
def new_attribute(request):
    """Handles page where we create new attribute"""
    if request.method == 'POST':
        form = AttributeForm(request.POST)
        if form.is_valid():
            attribute_name = form.cleaned_data['name']
            extent = set()
            for obj in form.cleaned_data['object_set']:
                extent.add(obj.name)
            try:
                e_.db.check_form_and_add_attribute(form, attribute_name, extent)
                annotation = u'New Attribute: {0}'.format(attribute_name)
                register_event(request.user, 'attr_add', annotation)
            except IllegalContextModification:
                display_error("""Provided attribute violates background
                                 knowledge and can not be saved.""")
            except NotUniqueAttributeName:
                display_error("""Attribute name should be unique.""")
            return HttpResponseRedirect('/')
    else:
        form = AttributeForm()

    return render_to_response(os.path.join(APP_NAME, 'attributes/new.html'),
                              {'form': form,},
                              context_instance=RequestContext(request))

@login_required                       
def edit_attribute(request, attribute_id):
    """Handles page where we edit attributes"""
    attribute = get_object_or_404(Attribute, pk=attribute_id)
    old_name = attribute.name
    if request.method == 'POST':
        if 'delete' in request.POST:
            attribute.delete()
            e_.db.sync_implications()
            register_event(request.user, 'attr_rem', attribute.name)
        else:
            form = AttributeForm(request.POST, instance=attribute)
            if form.is_valid():
                attribute_name = form.cleaned_data['name']
                extent = set()
                for obj in form.cleaned_data['object_set']:
                    extent.add(obj.name)
                try:
                    e_.db.check_form_and_edit_attribute(form, 
                                                        attribute_name,
                                                        old_name, 
                                                        extent)
                    annotation = u'Edited Attribute: {0}'.format(attribute_name)
                    register_event(request.user, 'attr_edit', annotation)
                except IllegalContextModification:
                    display_error("""Proposed attribute modification violates 
                                     background knowledge and can not be 
                                     saved.""")
                except NotUniqueObjectName:
                    display_error("""Attribute name should be unique.""")
        return HttpResponseRedirect('/')
    else:
        form = AttributeForm(instance=attribute)

    return render_to_response(os.path.join(APP_NAME, 'attributes/edit.html'),
                          {'form': form,
                           'attribute_id': attribute_id},
                          context_instance=RequestContext(request))

class UploadFileForm(forms.Form):
    import_context = forms.FileField()

@login_required                              
def import_context_view(request):
    """docstring for import_context"""
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            if request.user.is_authenticated():
                import_context(request.FILES['import_context'])
                e_.reload()
            else:
                # TODO: Show message
                pass
            return HttpResponseRedirect(reverse('exploration.views.main'))
    else:
        form = UploadFileForm()
    return render_to_response(os.path.join(APP_NAME, 'import.html'),
                              { "form" : form },
                              context_instance=RequestContext(request))
                              
@login_required
def export_view(request):
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = \
        'attachment; filename=context-{0}.csv'.format(datetime.date.today())
    response.write(get_csv())
    
    return response
    
@login_required
def accept_implication(request):
    if request.method == 'POST':
        pk = request.POST['pk']
        imp_object = AttributeImplication.objects.get(pk=pk)
        e_.exploration.confirm_implication(imp_object)
        # Here we need to return something like success flag and check it on 
        # client side
        return HttpResponseRedirect(reverse('exploration.views.main'))
    else:
        raise Http404
        
@login_required
def unaccept_implication(request):
    if request.method == 'POST':
        pk = request.POST['pk']
        imp_object = AttributeImplication.objects.get(pk=pk)
        e_.exploration.unconfirm_implication(imp_object)
        # Here we need to return something like success flag and check it on 
        # client side
        return HttpResponseRedirect(reverse('exploration.views.main'))
    else:
        raise Http404
        
@login_required
def get_premise(request):
    if request.method == 'POST':
        pk = request.POST['pk']
        imp_object = AttributeImplication.objects.get(pk=pk)
        premise = list(imp_object.get_premise())
        return HttpResponse(simplejson.dumps(premise, ensure_ascii=False), 
                                mimetype='application/json')
    else:
        raise Http404

@login_required
def add_counterexample(request):
    if request.method == 'POST':
        name = request.POST['Name']
        imp_pk = request.POST['imp_pk']
        imp_object = AttributeImplication.objects.get(pk=imp_pk)
        fca_imp = imp_object.get_fca_implication()
        attributes = Attribute.objects.all()
        
        intent = set()
        for attr in attributes:
            if attr.name in request.POST:
                intent.add(attr.name)
        intent = intent | fca_imp.premise
        e_.expert.learn_counterexample(name, intent)
        try:
            e_.exploration.reject_implication(fca_imp)
        except NotCounterexample:
            display_error("""Provided example is not valid counterexample to
                           implication: <br/>{0}<br/> 
                           Correct counterexample should not respect this 
                           implication.
                           """.format(fca_imp))
        except IllegalContextModification:
            display_error("""Provided counterexample violates background
                             knowledge and can not be saved.""")
        except NotUniqueObjectName:
            display_error("""Example name should be unique.""")
        return HttpResponseRedirect(reverse('exploration.views.main'))
    else:
        raise Http404
        
def display_error(message):
    global error_message
    error_message = message
    return HttpResponseRedirect(reverse('exploration.views.main'))